/*
 * ========================================================================
 *
 *       Filename:  flashcardframe.cpp
 *
 *    Description:  This is the implementation of the FlashCardFrame.
 *
 *        Version:  1.0
 *        Created:  05/11/2010 12:34:55 PM
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  Alex Avance (aa), aravance@gmail.com
 *        Company:  independent
 *
 * ========================================================================
 *
 * Copyright (C) 2010 Alex Avance
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-
 * 1307, USA.
 *
 * ========================================================================
 */
#include "misc.h"
#include "fcframe.h"
#include "wx/sizer.h"
#include "wx/msgdlg.h"
#include "wx/aboutdlg.h"

#include "flashcard.xpm"

#include <exception>

class save_fail: public std::exception
{
public:
  virtual const char* what () const throw ()
  { return "failed to save file"; }
} SaveFail;

BEGIN_EVENT_TABLE( FlashCardFrame, wxFrame )
  EVT_CLOSE( FlashCardFrame::ConfirmQuit )
  EVT_MENU( ID_Study, FlashCardFrame::OnStudy )
  EVT_MENU( ID_Test, FlashCardFrame::OnTest )
  EVT_MENU( ID_Reset, FlashCardFrame::OnReset )
  EVT_MENU( ID_SaveCard, FlashCardFrame::OnSaveCard )
  EVT_MENU( ID_DelCard, FlashCardFrame::OnDelCard )
  EVT_MENU( wxID_SAVE, FlashCardFrame::OnSave )
  EVT_MENU( wxID_SAVEAS, FlashCardFrame::OnSaveAs )
  EVT_MENU( wxID_OPEN, FlashCardFrame::OnOpen )
  EVT_MENU( wxID_CLOSE, FlashCardFrame::OnClose )
  EVT_MENU( ID_HideList, FlashCardFrame::OnHideList )
  EVT_MENU( ID_RandomSide, FlashCardFrame::OnRandomSide )
  EVT_MENU( ID_FrontSide, FlashCardFrame::OnFrontSide )
  EVT_MENU( ID_BackSide, FlashCardFrame::OnBackSide )
  EVT_MENU( ID_IgnoreCase, FlashCardFrame::OnIgnoreCase )
  EVT_MENU( ID_IgnorePunct, FlashCardFrame::OnIgnorePunct )
  EVT_MENU( ID_KeepRight, FlashCardFrame::OnKeepRight )
  EVT_MENU( wxID_EXIT, FlashCardFrame::OnQuit )
  EVT_MENU( wxID_ABOUT, FlashCardFrame::OnAbout )
  EVT_MENU( ID_License, FlashCardFrame::OnLicense )
END_EVENT_TABLE()

/**************************************************************************
Class Name: FlashCardFrame
Method Name: FlashCardFrame
Description: constructor
**************************************************************************/
FlashCardFrame::FlashCardFrame ( const wxString &title,
                                 const wxPoint &pos,
                                 const wxSize &size )
  : wxFrame( NULL, wxID_ANY, title, pos, size )
{
  SetIcon( wxICON( flashcard ) );

  // ==================== Menu Bar           =============================================
  m_MenuBar= new wxMenuBar;

  // define the items in the menu
  m_ItemStudy= new wxMenuItem( NULL, ID_Study, _("Stu&dy Board\tCtrl-Alt-S"),
                               _("Switch to the study board") );
  m_ItemTest= new wxMenuItem( NULL, ID_Test, _("&Test Board\tCtrl-Alt-T"),
                              _("Switch to the test board") );
  m_ItemReset= new wxMenuItem( NULL, ID_Reset, _("&Reset Decks\tCtrl-R"),
                                  _("Reset the test decks") );
  m_ItemSaveCard= new wxMenuItem( NULL, ID_SaveCard, _("Save &Card\tCtrl-Shift-S"),
                                  _("Save the current card") );
  m_ItemDelCard= new wxMenuItem( NULL, ID_DelCard, _("&Delete Card\tShift-Del"),
                                 _("Delete the current card") );
  m_ItemSave= new wxMenuItem( NULL, wxID_SAVE, _("&Save\tCtrl-S"),
                              _("Save the current deck(s)") );
  m_ItemSaveAs= new wxMenuItem( NULL, wxID_SAVEAS, _("Save &As..."),
                                _("Save the current deck(s) with different name") );
  m_ItemOpen= new wxMenuItem( NULL, wxID_OPEN, _("&Open\tCtrl-O"),
                              _("Load decks from a file") );
  m_ItemClose= new wxMenuItem( NULL, wxID_CLOSE, _("&Close\tCtrl-W"),
                               _("Unload current decks") );
  m_ItemQuit= new wxMenuItem( NULL, wxID_EXIT, QUIT_ENTRY,
                              _("Close the program") );
  m_ItemIgnoreCase= new wxMenuItem( NULL, ID_IgnoreCase, _("Ignore &Case\tCtrl-I"),
                                    _("Ignore case while guessing"), wxITEM_CHECK );
  m_ItemIgnorePunct= new wxMenuItem( NULL, ID_IgnorePunct, _("Ignore &Punctuation\tCtrl-P"),
                                     _("Ignore punctuation while guessing"), wxITEM_CHECK );
  m_ItemKeepRight= new wxMenuItem( NULL, ID_KeepRight, _("&Keep Right\tCtrl-K"),
                                   _("Don't remove correct guesses from the cycle"), wxITEM_CHECK );
  m_ItemRandomSide= new wxMenuItem( NULL, ID_RandomSide, _("&Randomize Side\tCtrl-R"),
                                    _("Randomly select side to ask"), wxITEM_RADIO );
  m_ItemFrontSide= new wxMenuItem( NULL, ID_FrontSide, _("&Front Side\tCtrl-F"),
                                    _("Always ask the front side"), wxITEM_RADIO );
  m_ItemBackSide= new wxMenuItem( NULL, ID_BackSide, _("&Back Side\tCtrl-B"),
                                    _("Always ask the back side"), wxITEM_RADIO );
  m_ItemHideList= new wxMenuItem( NULL, ID_HideList, _("&Hide List\tCtrl-H"),
                                  _("Hide the list of cards"), wxITEM_CHECK );
  m_ItemAbout= new wxMenuItem( NULL, wxID_ABOUT, _("&About\tF1"), _("Get info about the program") );
  m_ItemLicense= new wxMenuItem( NULL, ID_License, _("&License"), _("Get license information") );

  // append the items to the menu
  m_MenuFile= new wxMenu;
  m_MenuFile->Append( m_ItemTest );
  m_MenuFile->AppendSeparator();
  m_MenuFile->Append( m_ItemSaveCard );
  m_MenuFile->Append( m_ItemDelCard );
  m_MenuFile->AppendSeparator();
  m_MenuFile->Append( m_ItemSave );
  m_MenuFile->Append( m_ItemSaveAs );
  m_MenuFile->Append( m_ItemOpen );
  m_MenuFile->Append( m_ItemClose );
  m_MenuFile->AppendSeparator();
  m_MenuFile->Append( m_ItemQuit );
  m_MenuBar->Append( m_MenuFile, _("&File") );

  m_MenuStudySettings= new wxMenu;
  m_MenuStudySettings->Append( m_ItemHideList );
  m_MenuBar->Append( m_MenuStudySettings, _("&Settings") );

  m_MenuTestSettings= new wxMenu;
  m_MenuTestSettings->Append( m_ItemIgnoreCase );
  m_MenuTestSettings->Append( m_ItemIgnorePunct );
  m_MenuTestSettings->Append( m_ItemKeepRight );
  m_MenuTestSettings->AppendSeparator();
  m_MenuTestSettings->Append( m_ItemRandomSide );
  m_MenuTestSettings->Append( m_ItemFrontSide );
  m_MenuTestSettings->Append( m_ItemBackSide );
  m_ItemFrontSide->Check( true );

  m_MenuHelp= new wxMenu;
  m_MenuHelp->Append( m_ItemAbout );
  m_MenuHelp->Append( m_ItemLicense );
  m_MenuBar->Append( m_MenuHelp, _("&Help") );

  SetMenuBar( m_MenuBar );

  // ==================== Panel Setup        =============================================
  m_TestBoard= new TestBoardPanel( this, m_Deck );
  m_TestBoard->Hide();

  m_StudyBoard= new StudyBoardPanel( this, m_Deck );
  m_StudyBoard->Show();

  wxBoxSizer *sizer= new wxBoxSizer( wxVERTICAL );
  sizer->Add( m_TestBoard, 1, wxEXPAND, 0 );
  sizer->Add( m_StudyBoard, 1, wxEXPAND, 0 );

  SetSizer( sizer );

  m_FileDialog= new wxFileDialog( this, _("Choose a file"), _(""), _(""), FILE_WILDCARD,
                                  wxFD_OPEN | wxFD_MULTIPLE |
                                  wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR );

#if wxUSE_STATUSBAR
  CreateStatusBar();
  SetStatusText( _("") );
#endif
} // ----- end of method FlashCardFrame::FlashCardFrame (constructor) -----

/**************************************************************************
Class Name: FlashCardFrame
Method Name: ~FlashCardFrame
Description: destructor
**************************************************************************/
FlashCardFrame::~FlashCardFrame ()
{
  m_MenuBar->Remove( g_StudyMenuPos );

  if ( m_StudyBoard->IsShown() )
    m_MenuFile->Remove( m_ItemTest );
  else
    m_MenuFile->Remove( m_ItemStudy );

  delete m_MenuStudySettings;
  delete m_MenuTestSettings;
  delete m_ItemStudy;
  delete m_ItemTest;
  delete m_FileDialog;
} // ----- end of method FlashCardFrame::~FlashCardFrame (destructor) -----

/**************************************************************************
Class: FlashCardFrame
Method: Save
Description: Save the current deck(s).
**************************************************************************/
bool FlashCardFrame::Save ()
{
  // if there's only one filename, force boards to save and reset, and save it
  if ( m_FileNames.Count() == 1 )
    if ( !SaveDeck( m_FileNames[0], m_Deck ) )
      throw SaveFail;
    else
    {
      m_TestBoard->Unmodified();
      m_StudyBoard->Unmodified();
      return true;
    }
  // if there are none or multiples, use SaveAs()
  else
    return SaveAs();
} // ----- end of method FlashCardFrame::Save -----

/**************************************************************************
Class: FlashCardFrame
Method: SaveAs
Description: Save the current deck(s) with a different name.
**************************************************************************/
bool FlashCardFrame::SaveAs ()
{
  wxString defaultFile= ( m_FileNames.Count() == 0 ) ? _("") : m_FileNames[0].c_str();

  // ask for a deck name
  wxString filename= wxFileSelector( _("Choose a File"), m_FileDialog->GetDirectory(),
                                     defaultFile, _(".deck"), FILE_WILDCARD,
                                     wxFD_SAVE | wxFD_OVERWRITE_PROMPT, this );

  // if they didn't cancel, use Save() to do the work
  if ( !filename.IsEmpty() )
  {
    m_FileNames.Clear();
    m_FileNames.Add( filename );
    return Save();
  }
  else
    return false;
} // ----- end of method FlashCardFrame::SaveAs -----

/**************************************************************************
Class: FlashCardFrame
Method: Open
Description: This is used to open a deck file.
**************************************************************************/
bool FlashCardFrame::Open ( const wxString &filename )
{
  StudyDeck temp;

  if ( LoadDeck( filename, temp ) )
  {
    temp.Sort();
    m_Deck.Append( temp );

    return true;
  }
  else
    return false;
} // ----- end of method FlashCardFrame::Open -----

/**************************************************************************
Class: FlashCardFrame
Method: Show
Description: This is the show function
**************************************************************************/
bool FlashCardFrame::Show ( bool show )
{
  m_TestBoard->Update();
  m_StudyBoard->Update();
  return wxFrame::Show( show );
} // ----- end of method FlashCardFrame::Show -----

/**************************************************************************
Class: FlashCardFrame
Method: ConfirmQuit
Description: Confirm before quitting.
**************************************************************************/
void FlashCardFrame::ConfirmQuit ( wxCloseEvent &event )
{
  // if it's vetoable
  if ( event.CanVeto() )
    try
    {
      int result= wxYES;

      m_TestBoard->ConfirmSwitch( true );
      m_StudyBoard->ConfirmSwitch( true );

      // ask about saving
      if ( m_StudyBoard->IsModified() || m_TestBoard->IsModified() )
      {
        result= wxMessageBox( _("The deck has been modified, save first?"),
                             _("Confirm Save"),
                             wxYES_NO | wxCANCEL | wxICON_QUESTION, this );

        if ( result == wxYES )
          Save();
      }

      // if it canceled, veto
      if ( result == wxCANCEL )
        event.Veto();
      // otherwise just destroy
      else
        Destroy();
    }
    catch ( save_fail exc )
    {
      wxMessageBox( wxString::Format( _("Error: %s"), exc.what() ),
                    _("Error"), wxOK | wxICON_ERROR, this );
      event.Veto();
    }
  else
    Destroy();
} // ----- end of method FlashCardFrame::ConfirmQuit -----

/**************************************************************************
Class: FlashCardFrame
Method: OnStudy
Description: This switches to the study board.
**************************************************************************/
void FlashCardFrame::OnStudy ( wxCommandEvent &event )
{
  // if study board is hidden
  if ( !m_StudyBoard->IsShown() && m_TestBoard->ConfirmSwitch() )
  {
    size_t fileMenuPos= 0;

    // hide the test board and show study board
    m_StudyBoard->Show();
    m_TestBoard->Hide();

    // rearrange menus accordingly
    m_MenuFile->Remove( m_ItemStudy );
    m_MenuFile->Remove( wxID_SEPARATOR );
    m_MenuFile->Remove( m_ItemReset );
    m_MenuFile->Insert( fileMenuPos++, m_ItemTest );
    m_MenuFile->InsertSeparator( fileMenuPos++ );
    m_MenuFile->Insert( fileMenuPos++, m_ItemSaveCard );
    m_MenuFile->Insert( fileMenuPos++, m_ItemDelCard );

    m_MenuBar->Replace( g_StudyMenuPos, m_MenuStudySettings, _("&Settings") );

    Layout();
  }
} // ----- end of method FlashCardFrame::OnStudy -----

/**************************************************************************
Class: FlashCardFrame
Method: OnTest
Description: This switches to the test board.
**************************************************************************/
void FlashCardFrame::OnTest ( wxCommandEvent &event )
{
  // if test board is hidden
  if ( !m_TestBoard->IsShown() && m_StudyBoard->ConfirmSwitch() )
  {
    size_t fileMenuPos= 0;

    // hide the study board and show test board
    m_TestBoard->Show();
    m_StudyBoard->Hide();

    // rearrange menus accordingly
    m_MenuFile->Remove( m_ItemTest );
    m_MenuFile->Remove( wxID_SEPARATOR );
    m_MenuFile->Remove( m_ItemSaveCard );
    m_MenuFile->Remove( m_ItemDelCard );
    m_MenuFile->Insert( fileMenuPos++, m_ItemStudy );
    m_MenuFile->InsertSeparator( fileMenuPos++ );
    m_MenuFile->Insert( fileMenuPos++, m_ItemReset );

    m_MenuBar->Replace( g_StudyMenuPos, m_MenuTestSettings, _("&Settings") );

    Layout();
  }
} // ----- end of method FlashCardFrame::OnTest -----

/**************************************************************************
Class: FlashCardFrame
Method: OnReset
Description: Handle reset events.
**************************************************************************/
void FlashCardFrame::OnReset ( wxCommandEvent &event )
{
  m_TestBoard->OnReset( event );
} // ----- end of method FlashCardFrame::OnReset -----

/**************************************************************************
Class: FlashCardFrame
Method: OnSaveCard
Description: Handle save card events.
**************************************************************************/
void FlashCardFrame::OnSaveCard ( wxCommandEvent &event )
{
  m_StudyBoard->OnSaveCard( event );
} // ----- end of method FlashCardFrame::OnSaveCard -----

/**************************************************************************
Class: FlashCardFrame
Method: OnDelCard
Description: Handle delete card events.
**************************************************************************/
void FlashCardFrame::OnDelCard ( wxCommandEvent &event )
{
  m_StudyBoard->OnDelCard( event );
} // ----- end of method FlashCardFrame::OnDelCard -----

/**************************************************************************
Class: FlashCardFrame
Method: OnSave
Description: Save the current deck(s).
**************************************************************************/
void FlashCardFrame::OnSave ( wxCommandEvent &event )
{
  // call Save() to do the work
  try
  {
    Save();
  }
  catch ( save_fail exc )
  {
    wxMessageBox( wxString::Format( _("Error: %s"), exc.what() ),
                  _("Error"), wxOK | wxICON_ERROR, this );
  }
} // ----- end of method FlashCardFrame::OnSave -----

/**************************************************************************
Class: FlashCardFrame
Method: OnSaveAs
Description: Save the current deck(s) with a different name.
 **************************************************************************/
void FlashCardFrame::OnSaveAs ( wxCommandEvent &event )
{
  // call SaveAs() to do the work
  try
  {
    SaveAs();
  }
  catch ( save_fail exc )
  {
    wxMessageBox( wxString::Format( _("Error: %s"), exc.what() ),
                  _("Error"), wxOK | wxICON_ERROR, this );
  }
} // ----- end of method FlashCardFrame::OnSaveAs -----

/**************************************************************************
Class: FlashCardFrame
Method: OnOpen
Description: Open deck(s) from file(s).
**************************************************************************/
void FlashCardFrame::OnOpen ( wxCommandEvent &event )
{
  m_TestBoard->ConfirmSwitch( true );
  m_StudyBoard->ConfirmSwitch( true );

  // ask for file(s) to open
  int result= m_FileDialog->ShowModal();

  // if they didn't cancel, reset the boards and open the decks
  if ( result != wxID_CANCEL )
  {
    wxArrayString filenames, failedFiles;
    StudyDeck temp;

    m_FileDialog->GetPaths( filenames );

    while ( !filenames.IsEmpty() )
    {
      if ( !Open( filenames[0] ) )
        failedFiles.Add( filenames[0].AfterLast( wxT('/') ) );
      else
        m_FileNames.Add( filenames[0] );

      filenames.RemoveAt( 0 );
    }

    if ( !failedFiles.IsEmpty() )
    {
      wxString msg;

      if ( failedFiles.Count() == 1 )
        msg.Printf( _("Failed to open file: \"%s\""), failedFiles[0].c_str() );
      else
      {
        msg= _("Failed to open files:");

        for ( size_t i= 0; i < failedFiles.Count(); ++i )
        {
          msg.Append( _("\n") );
          msg.Append( failedFiles[i] );
        }
      }

      wxMessageBox( msg, _("Failure"), wxOK | wxICON_ERROR, this );
    }

    m_TestBoard->Update();
    m_StudyBoard->Update();
  }
} // ----- end of method FlashCardFrame::OnOpen -----

/**************************************************************************
Class: FlashCardFrame
Method: OnClose
Description: Close the current deck(s).
**************************************************************************/
void FlashCardFrame::OnClose ( wxCommandEvent &event )
{
  int result= wxYES;

  // if either board is modified, confirm first
  if ( m_StudyBoard->IsModified() || m_TestBoard->IsModified() )
  {
    result= wxMessageBox( _("The deck has been modified, save first?"),
                          _("Confirm Save"),
                          wxYES_NO | wxCANCEL | wxICON_QUESTION, this );
    if ( result == wxYES )
      Save();
  }

  // if not save fail and not cancel, reset the boards and unload decks
  if ( result != wxCANCEL && result != SAVE_FAIL )
  {
    m_Deck.Clear();

    m_StudyBoard->Reset();
    m_StudyBoard->Unmodified();

    m_TestBoard->Reset();
    m_TestBoard->Unmodified();
  }
} // ----- end of method FlashCardFrame::OnClose -----

/**************************************************************************
Class: FlashCardFrame
Method: OnHideList
Description: Hide the list in the study board frame.
**************************************************************************/
void FlashCardFrame::OnHideList ( wxCommandEvent &event )
{
  m_StudyBoard->OnHideList( event );
} // ----- end of method FlashCardFrame::OnHideList -----

/**************************************************************************
Class: FlashCardFrame
Method: OnRandomSide
Description: Handle the RandomSide menu event.
**************************************************************************/
void FlashCardFrame::OnRandomSide ( wxCommandEvent &event )
{
  m_TestBoard->OnRandomSide( event );
} // ----- end of method FlashCardFrame::OnRandomSide -----

/**************************************************************************
Class: FlashCardFrame
Method: OnFrontSide
Description: Handle the FrontSide menu event.
**************************************************************************/
void FlashCardFrame::OnFrontSide ( wxCommandEvent &event )
{
  m_TestBoard->OnFrontSide( event );
} // ----- end of method FlashCardFrame::OnFrontSide -----

/**************************************************************************
Class: FlashCardFrame
Method: OnBackSide
Description: Handle the BackSide menu event.
**************************************************************************/
void FlashCardFrame::OnBackSide ( wxCommandEvent &event )
{
  m_TestBoard->OnBackSide( event );
} // ----- end of method FlashCardFrame::OnBackSide -----

/**************************************************************************
Class: FlashCardFrame
Method: OnIgnoreCase
Description: Handle the IgnoreCase menu event.
**************************************************************************/
void FlashCardFrame::OnIgnoreCase ( wxCommandEvent &event )
{
  m_TestBoard->OnIgnoreCase( event );
} // ----- end of method FlashCardFrame::OnIgnoreCase -----

/**************************************************************************
Class: FlashCardFrame
Method: OnIgnorePunct
Description: Handle the IgnorePunct menu event.
**************************************************************************/
void FlashCardFrame::OnIgnorePunct ( wxCommandEvent &event )
{
  m_TestBoard->OnIgnorePunct( event );
} // ----- end of method FlashCardFrame::OnIgnorePunct -----

/**************************************************************************
Class: FlashCardFrame
Method: OnKeepRight
Description: Handle the KeepRight menu event.
**************************************************************************/
void FlashCardFrame::OnKeepRight ( wxCommandEvent &event )
{ m_TestBoard->OnKeepRight( event ); } // ----- end of method FlashCardFrame::OnKeepRight -----

/**************************************************************************
Class: FlashCardFrame
Method: OnQuit
Description: Close the program, confirming first.
**************************************************************************/
void FlashCardFrame::OnQuit ( wxCommandEvent &event )
{
  // non forced close
  Close( false );
} // ----- end of method FlashCardFrame::OnQuit -----

/**************************************************************************
Class: FlashCardFrame
Method: OnAbout
Description: This gives about information for the program.
**************************************************************************/
void FlashCardFrame::OnAbout ( wxCommandEvent &event )
{
  wxAboutDialogInfo dialogInfo;

  dialogInfo.SetVersion( wxString( VERSION_STRING, wxConvUTF8 ) );
  dialogInfo.SetCopyright( wxString( COPYRIGHT_STRING, wxConvUTF8 ) );
  dialogInfo.SetDescription( wxString( DESCRIPTION_STRING, wxConvUTF8 ) );

  wxAboutBox( dialogInfo );
} // ----- end of method FlashCardFrame::OnAbout -----

/**************************************************************************
Class: FlashCardFrame
Method: OnLicense
Description: This gives license information for the program.
**************************************************************************/
void FlashCardFrame::OnLicense ( wxCommandEvent &event )
{
  wxAboutDialogInfo dialogInfo;

  dialogInfo.SetCopyright( wxString( LICENSE_STRING, wxConvUTF8 ) );

  wxAboutBox( dialogInfo );
} // ----- end of method FlashCardFrame::OnLicense -----
